unit ModelView;                                                                                   

interface

uses
  ExtCtrls,
  Voxel,
  VectorTypes,
  Graphics;

type
  TModelView = class
  private
    FZoom: Integer;
    procedure AdjustCanvasSize;
  public
    Model: TVoxelSection;
    Image: TBitmap;
    Mode: EModelViewMode;
    // Spec: ESpectrumMode;
    Orient: EModelViewOrient;
    Width, Height: Byte;
    Focus: ^TVector3i;
    WidthOnScreen, HeightOnScreen: Integer;

    procedure SetZoom(Value: Integer);

    property Zoom: Integer read FZoom write SetZoom;

    constructor Create(Model: TVoxelSection; var Focus: TVector3i;
      Orient: EModelViewOrient);

    procedure ChangeOrient(Orient: EModelViewOrient);
    procedure TranslateCoord2D(X, Y: Integer; var Dest: TVector3i);
    procedure TranslateCoord3D(X, Y, Z: Integer; var Dest: TVector2i);
    procedure Paint;
    procedure PaintCell(X, Y: Integer);
    procedure OverlayCell(X, Y: Integer; Color: TColor);
    procedure HighlightFocus;
    procedure Blit(Dest: TCanvas); // overload;
    // procedure Blit(X, Y: Integer; Dest: TCanvas); overload;
  end;

  PModelView = ^TModelView;

implementation

uses
  Types,
  Math,
  Palette,
  Voxel_Engine;

{ TModelView Member Functions }

procedure TModelView.SetZoom(Value: Integer);
begin
  FZoom := Value;
  AdjustCanvasSize;
end;

constructor TModelView.Create(Model: TVoxelSection; var Focus: TVector3i;
  Orient: EModelViewOrient);
begin
  Self.Model := Model;
  Self.Focus := @Focus;
  Self.Orient := Orient;
  Mode := vmEmphasiseDepth;
  // Spec := smColors;
  FZoom := 16;
  Image := TBitmap.Create;
  ChangeOrient(Orient);
end;

procedure TModelView.AdjustCanvasSize;
begin
  WidthOnScreen := Width * Zoom;
  HeightOnScreen := Height * Zoom;
  with Image do
  begin
    Width := WidthOnScreen;
    Height := HeightOnScreen;
  end;
end;

procedure TModelView.HighlightFocus;
var
  c: TVector2i;
  r: TRect;
begin
  TranslateCoord3D(Focus.X, Focus.Y, Focus.Z, c);
  with Image.Canvas do
  begin
    r.Left := c.X*FZoom+(FZoom shr 1);
    r.Right := r.Left+2;
    r.Top := 0;
    r.Bottom := HeightOnScreen;
    DrawFocusRect(r);
    r.Left := 0;
    r.Right := WidthOnScreen;
    r.Top := c.Y*FZoom+(FZoom shr 1);
    r.Bottom := r.Top+2;
    DrawFocusRect(r);
  end;
end;

procedure TModelView.ChangeOrient(Orient: EModelViewOrient);
begin
  Self.Orient := Orient;
  case Orient of
    voTop:
    begin
      Width := Model.Tailer.XSize; Height := Model.Tailer.YSize;
    end;
    voRight:
    begin
      Width := Model.Tailer.XSize; Height := Model.Tailer.ZSize;
    end;
    voBack:
    begin
      Width := Model.Tailer.YSize; Height := Model.Tailer.ZSize;
    end;
  end;
  AdjustCanvasSize;
end;

procedure TModelView.TranslateCoord2D(X, Y: Integer; var Dest: TVector3i);
begin
  case Orient of
    voTop:
    begin
      Dest.X := X; Dest.Y := Y; Dest.Z := Focus.Z;
    end;
    voRight:
    begin
      Dest.X := X; Dest.Y := Focus.Y; Dest.Z := Model.Tailer.ZSize-1-Y;
    end;
    voBack:
    begin
      Dest.X := Focus.X; Dest.Y := Model.Tailer.YSize-1-X; Dest.Z := Model.Tailer.ZSize-1-Y;
    end;
  end;
end;

procedure TModelView.TranslateCoord3D(X, Y, Z: Integer; var Dest: TVector2i);
begin
  case Orient of
    voTop:
    begin
      Dest.X := X; Dest.Y := Y;
    end;
    voRight:
    begin
      Dest.X := X; Dest.Y := Height-1-Z;
    end;
    voBack:
    begin
      Dest.X := Width-1-Y; Dest.Y := Height-1-Z;
    end;
  end;
end;

procedure TModelView.Paint;
var
  r: TRect;
  i, j, k: Integer;
  v: TVoxel;
  ThisLayer: Boolean;
begin
  for i:=0 to Width-1 do
  begin
    for j:=0 to Height-1 do
    begin
      PaintCell(i, j);
    end;
  end;
end;

procedure TModelView.PaintCell(X, Y: Integer);
var
  r: TRect;
  k: Integer;
  v: TVoxel;
  ThisLayer: Boolean;
begin
  r.Left := X*FZoom;
  r.Right := r.Left+FZoom;
  r.Top := Y*FZoom;
  r.Bottom := r.Top+FZoom;
  if Mode = vmEmphasiseDepth then
  begin
    ThisLayer := False;
    case Orient of
      voTop:
      begin
        for k:=Focus.Z downto 0 do
        begin
          Model.GetVoxel(X, Y, k, v);
          if v.Used then Break;
        end;
        if k = Focus.Z then ThisLayer := True;
      end;
      voRight:
      begin
        for k:=Focus.Y to Model.Tailer.YSize-1 do
        begin
          Model.GetVoxel(X, k, Height-1-Y, v);
          if v.Used then Break;
        end;
        if k = Focus.Y then ThisLayer := True;
      end;
      voBack:
      begin
        for k:=Focus.X to Model.Tailer.XSize-1 do
        begin
          Model.GetVoxel(k, Width-1-X, Height-1-Y, v);
          if v.Used then Break;
        end;
        if k = Focus.X then ThisLayer := True;
      end;
    end;
    if not v.Used then
    begin
      Image.Canvas.Brush.Color := BGViewColor;
      Image.Canvas.FillRect(r);
    end
    else
    begin
      Image.Canvas.Pen.Color := clRed;
      Image.Canvas.Brush.Color := VxlPalette[v.Color];
      if ThisLayer then Image.Canvas.Rectangle(r)
      else Image.Canvas.FillRect(r)
    end;
  end
  else
  begin
    case Orient of
      voTop:
    Model.GetVoxel(X, Y, Focus.Z, v);
      voRight:
    Model.GetVoxel(X, Focus.Y, Height-1-Y, v);
      voBack:
    Model.GetVoxel(Focus.X, Width-1-X, Height-1-Y, v);
    end;
    if v.Used then Image.Canvas.Brush.Color := VxlPalette[v.Color]
    else Image.Canvas.Brush.Color := BGViewColor;
    Image.Canvas.FillRect(r);
  end;
end;

procedure TModelView.OverlayCell;
var
  r: TRect;
begin
  Image.Canvas.Pen.Color := clRed;
  Image.Canvas.Brush.Color := Color;
  r.Left := X*FZoom;
  r.Right := r.Left+FZoom;
  r.Top := Y*FZoom;
  r.Bottom := r.Top+FZoom;
  Image.Canvas.Rectangle(r);
end;

procedure TModelView.Blit(Dest: TCanvas);
begin
  Dest.Draw(0, 0, Image);
end;

{procedure TModelView.Blit(X, Y: Integer; Dest: TCanvas);
var
  r: TRect;
begin
  r.Left := X*FZoom;
  r.Right := r.Left+FZoom;
  r.Top := Y*FZoom;
  r.Bottom := r.Top+FZoom;
  Dest.CopyRect(r, Image.Canvas, r);
end;}

end.
